package service

import (
	"errors"
	"strings"
	"time"

	"gitee.com/sansaniot/ssiot-core/httpmvc/service"
	"gorm.io/gorm"

	"ssdevice/common/constant"
	cDto "ssdevice/common/dto"
	"ssdevice/common/enums"
	"ssdevice/common/utils"
	"ssdevice/internal/device/models"
	"ssdevice/internal/device/models/command"
	"ssdevice/internal/device/models/query"
)

type Product struct {
	devProductModel *models.SysDevProduct
	devSchemaModel  *models.SysDevSchema
	devConfigModel  *models.SysDevConfig
	devModel        *models.SysDev
	variableModel   *models.SysProductVariable
	service.Service
}

func (d *Product) SetProductModel(m *models.SysDevProduct) *Product {
	d.devProductModel = m
	return d
}

func (d *Product) SetSchemaModel(m *models.SysDevSchema) *Product {
	d.devSchemaModel = m
	return d
}

func (d *Product) SetConfigModel(m *models.SysDevConfig) *Product {
	d.devConfigModel = m
	return d
}

func (d *Product) SetDevModel(m *models.SysDev) *Product {
	d.devModel = m
	return d
}

func (d *Product) SetVariableModel(m *models.SysProductVariable) *Product {
	d.variableModel = m
	return d
}

func (d *Product) SetAllModel() {
	d.devProductModel = &models.SysDevProduct{}
	d.devSchemaModel = &models.SysDevSchema{}
	d.devConfigModel = &models.SysDevConfig{}
	d.devModel = &models.SysDev{}
	d.variableModel = &models.SysProductVariable{}
	d.devProductModel.Orm = d.Orm
	d.devSchemaModel.Orm = d.Orm
	d.devConfigModel.Orm = d.Orm
	d.devModel.Orm = d.Orm
	d.variableModel.Orm = d.Orm
}

func generateProductSchema(product models.SysDevProduct, devSchema *models.SysDevSchema) *models.SysDevSchema {
	now := cDto.LocalTime(time.Now())
	schemaData := []byte(utils.If(product.DeviceType == enums.DevTypeOfDevice, constant.DevSchemaTemplate, constant.GatewaySchemaTemplate).(string))
	if devSchema != nil {
		schemaData = devSchema.Schema
	}
	return &models.SysDevSchema{
		Id:           utils.SimpleUUID(),
		Schema:       schemaData,
		ProductModel: product.ProductModel,
		DevProductId: product.Id,
		CreateTime:   &now,
		UpdateTime:   &now,
	}
}

func generateProductConfig(product models.SysDevProduct, devConfig *models.SysDevConfig) *models.SysDevConfig {
	now := cDto.LocalTime(time.Now())
	configData := []byte(utils.If(product.DeviceType == enums.DevTypeOfDevice, constant.DevConfigTemplate, constant.GatewayConfigTemplate).(string))
	if devConfig != nil {
		configData = devConfig.Data
	}
	return &models.SysDevConfig{
		Id:           utils.SimpleUUID(),
		Data:         configData,
		ProductModel: product.ProductModel,
		DevProductId: product.Id,
		CreateTime:   &now,
		UpdateTime:   &now,
	}
}

// 添加产品
func (d *Product) AddProduct(req *command.ProductInsertReq) (err error, product models.SysDevProduct) {
	if len(req.UserId) > 0 && req.DeptId == constant.PublicGroupId && req.UserId != "1" {
		err = errors.New("没权限操作公共分组资源")
		return
	}
	if err = req.Generate(&product); err != nil {
		return
	}
	err = d.Orm.Transaction(func(tx *gorm.DB) error {
		// 事务orm
		d.devProductModel.Orm = tx
		d.devSchemaModel.Orm = tx
		d.devConfigModel.Orm = tx
		d.variableModel.Orm = tx
		// 产品基本信息
		if err = d.devProductModel.AddProduct(&product); err != nil {
			return err
		}
		// 产品schema
		addSchema := generateProductSchema(product, req.DevSchema)
		if err = d.devSchemaModel.Add(addSchema); err != nil {
			return err
		}
		// 产品config
		addConfig := generateProductConfig(product, req.DevConfig)
		if err = d.devConfigModel.Add(addConfig); err != nil {
			return err
		}
		// 产品变量
		vars := d.variableModel.GenFromSchemaProfile(product.ProductModel, utils.BytesToMap(addSchema.Schema), utils.BytesToMap(addConfig.Data), nil)
		if len(vars) > 0 {
			if err = d.variableModel.Add(&vars); err != nil {
				return err
			}
		}
		return nil
	})
	return
}

// 编辑产品
func (d *Product) EditProduct(req *command.ProductEditReq) (err error) {
	if err = d.devProductModel.FindOne(d.devProductModel, cDto.MakeCondition(query.SysProductQuery{Id: req.Id})); err != nil {
		return
	}
	if d.devProductModel.DeptId == constant.PublicGroupId && req.UserId != "1" {
		return errors.New("没权限操作公共分组资源")
	}
	return d.devProductModel.ModProduct(req.GenerateMap())
}

// 删除产品
func (d *Product) DeleteProduct(req *command.ProductDeleteReq) (err error) {
	if err = d.Orm.Transaction(func(tx *gorm.DB) error {
		// 事务orm
		d.devModel.Orm = tx
		d.devProductModel.Orm = tx
		d.devSchemaModel.Orm = tx
		d.devConfigModel.Orm = tx
		d.variableModel.Orm = tx
		// 批量操作
		delIds := strings.Split(strings.ReplaceAll(req.Id, " ", ""), ",")
		var delModels []string
		if delModels, err = d.devProductModel.GetIdModels(delIds, req.UserId); err != nil {
			return err
		}
		// 置空关联设备的productModel字段
		if err = d.devProductModel.SetRelateDevCodeEmpty(delIds); err != nil {
			return err
		}
		// 产品信息
		if err = d.devProductModel.Delete(d.devProductModel, cDto.MakeCondition(query.SysProductQuery{IdIn: delIds})); err != nil {
			return err
		}
		// 产品schema
		if err = d.devSchemaModel.Delete(d.devSchemaModel, cDto.MakeCondition(query.SysDevSchemaQuery{ProductIdIn: delIds})); err != nil {
			return err
		}
		// 产品config
		if err = d.devConfigModel.Delete(d.devConfigModel, cDto.MakeCondition(query.SysDevConfigQuery{ProductIdIn: delIds})); err != nil {
			return err
		}
		// 产品变量
		if len(delModels) > 0 {
			if err = d.variableModel.Delete(&models.SysProductVariable{}, cDto.MakeCondition(query.SysProductVariableQuery{ProductModelIn: delModels})); err != nil {
				return err
			}
		}
		return nil
	}); err != nil {
		return
	}
	return
}

// 产品复制
func (d *Product) ProductCopy(req *command.ProductCopyReq) (one models.SysDevProduct, err error) {
	product := models.SysDevProduct{}
	if err = d.devProductModel.FindOne(&product, cDto.MakeCondition(query.SysProductQuery{Id: req.Id})); err != nil {
		return
	}
	if product.DeptId == constant.PublicGroupId && req.UserId != "1" {
		err = errors.New("没权限操作公共分组资源")
		return
	}
	productInsert := command.ProductInsertReq{}
	req.GenerateProduct(&product, &productInsert)
	// 查询产品schema
	if err = d.devSchemaModel.FindOne(d.devSchemaModel, cDto.MakeCondition(query.SysDevSchemaQuery{ProductId: product.Id})); err != nil {
		return
	}
	// 查询产品config
	if err = d.devConfigModel.FindOne(d.devConfigModel, cDto.MakeCondition(query.SysDevConfigQuery{ProductId: product.Id})); err != nil {
		return
	}
	productInsert.DevSchema = d.devSchemaModel
	productInsert.DevConfig = d.devConfigModel
	// 添加产品
	sp := &Product{}
	sp.Orm = d.Orm
	sp.SetAllModel()
	err, one = sp.AddProduct(&productInsert)
	return
}

// 产品设备同步更新
func (d *Product) ProductDevSync(req *command.ProductDevSyncReq) (err error) {
	if err = d.devProductModel.FindOne(d.devProductModel, cDto.MakeCondition(query.SysProductQuery{ProductModel: req.ProductModel})); err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			err = nil
		}
		return
	}
	if d.devProductModel.DeptId == constant.PublicGroupId && req.UserId != "1" {
		err = errors.New("没权限操作公共分组资源")
		return
	}
	// 该产品的设备s
	devList := make([]models.SysDev, 0)
	if err = d.devModel.FindList(&devList, cDto.MakeCondition(query.SysDevQuery{Sn: req.Sn, ProductModel: d.devProductModel.ProductModel})); err != nil {
		return
	}
	if len(devList) == 0 {
		return
	}
	snList := make([]string, 0)
	snDevMap := make(map[string]models.SysDev)
	for _, dev := range devList {
		snList = append(snList, dev.Sn)
		snDevMap[dev.Sn] = dev
	}
	err = d.Orm.Transaction(func(tx *gorm.DB) error {
		sensorM := &models.SysDevSensor{}
		systemM := &models.SysDevSystem{}
		devVarM := &models.SysDevVariable{}
		sensorM.Orm = tx
		systemM.Orm = tx
		devVarM.Orm = tx
		d.devSchemaModel.Orm = tx
		d.devConfigModel.Orm = tx
		// 设备schema
		if err = d.devSchemaModel.Delete(d.devSchemaModel, cDto.MakeCondition(query.SysDevSchemaQuery{SnIn: snList})); err != nil {
			return err
		}
		// 设备profile.config
		if err = d.devConfigModel.Delete(d.devConfigModel, cDto.MakeCondition(query.SysDevConfigQuery{SnIn: snList})); err != nil {
			return err
		}
		// 设备profile.sensor
		if err = sensorM.Delete(sensorM, cDto.MakeCondition(query.SysDevSensorQuery{SnIn: snList})); err != nil {
			return err
		}
		// 设备profile.system
		if err = systemM.Delete(systemM, cDto.MakeCondition(query.SysDevSystemQuery{SnIn: snList})); err != nil {
			return err
		}
		// 设备变量
		if err = devVarM.Delete(devVarM, cDto.MakeCondition(query.SysDevVariableQuery{SnIn: snList})); err != nil {
			return err
		}

		// 根据产品生成
		if err = d.devSchemaModel.FindOne(d.devSchemaModel, cDto.MakeCondition(query.SysDevSchemaQuery{ProductId: d.devProductModel.Id})); err != nil {
			return err
		}
		if err = d.devConfigModel.FindOne(d.devConfigModel, cDto.MakeCondition(query.SysDevConfigQuery{ProductId: d.devProductModel.Id})); err != nil {
			return err
		}
		schemas := make([]models.SysDevSchema, 0)
		configs := make([]models.SysDevConfig, 0)
		sensors := make([]models.SysDevSensor, 0)
		systems := make([]models.SysDevSystem, 0)
		varibs := make([]models.SysDevVariable, 0)
		now := cDto.LocalTime(time.Now())
		varKeyMap := GetProductVarKeyIndex(tx, d.devProductModel.ProductModel)
		for _, sn := range snList {
			// 复制成设备schema
			d.devSchemaModel.Id = utils.SimpleUUID()
			d.devSchemaModel.DevSn = sn
			d.devSchemaModel.CreateTime = &now
			d.devSchemaModel.UpdateTime = &now
			d.devSchemaModel.ProductModel = ""
			d.devSchemaModel.DevProductId = ""
			dev := snDevMap[sn]
			// 特性
			devConfig, devSensor, devSystem := generateProfile(tx, &dev, *d.devConfigModel, &command.DeviceInsertReq{})
			// 设备变量
			vars := devVarM.GenFromSchemaProfile(dev.Sn, utils.BytesToMap(d.devSchemaModel.Schema), utils.BytesToMap(devConfig.Data), varKeyMap)
			// 待添加覆盖
			schemas = append(schemas, *d.devSchemaModel)
			configs = append(configs, *devConfig)
			sensors = append(sensors, *devSensor)
			systems = append(systems, *devSystem)
			if len(vars) > 0 {
				varibs = append(varibs, vars...)
			}
		}
		// 增加
		if err = d.devSchemaModel.Add(&schemas); err != nil {
			return err
		}
		// 设备profile.config
		if err = d.devConfigModel.Add(&configs); err != nil {
			return err
		}
		// 设备profile.sensor
		if err = sensorM.Add(&sensors); err != nil {
			return err
		}
		// 设备profile.system
		if err = systemM.Add(&systems); err != nil {
			return err
		}
		if err = devVarM.Add(&varibs); err != nil {
			return err
		}
		return nil
	})
	return
}
